package com.github.xzb617.client.flow.core.hotparam;

import com.github.xzb617.client.flow.cache.CounterCache;
import com.github.xzb617.client.flow.constant.CacheKey;
import com.github.xzb617.client.flow.core.FlowContext;
import com.github.xzb617.client.flow.core.FlowException;
import com.github.xzb617.client.flow.core.FlowLimiter;
import com.github.xzb617.client.flow.core.FlowProperties;
import com.github.xzb617.client.flow.utils.DateUtil;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * 热点参数限流器
 * <p>
 *     1.热点参数目前只支持GET、DELETE请求
 *     2.每个路径目前只支持设定一个热点参数
 * </p>
 * @author xzb617
 */
public class HotParamFlowLimiter implements FlowLimiter {

    @Override
    public boolean enabled(FlowProperties flowConfig) {
        FlowProperties.HotParamProperties hotParam = flowConfig.getParam();
        return Boolean.TRUE.equals(hotParam.getEnabled());
    }

    @Override
    public void executeInternal(FlowContext flowContext, FlowProperties flowConfig, CounterCache counterCache, FlowLimiter limiterChain) throws FlowException {
        Map<String, Map<String, Object>> rules = flowConfig.getParam().getRules();
        String uri = flowContext.getUri();
        Map<String, Object> paramRule = rules.get(uri);
        // 判断该规则是否在其有效时间内
        boolean isEffectiveRule = paramRule!=null && DateUtil.isEffectiveTime(HotParamRule.getStartTime(paramRule), HotParamRule.getFinishTime(paramRule));
        if (isEffectiveRule) {
            // 热点参数值匹配
            boolean matched = ParamRuleMatcher.matched(flowContext.getQueryParams(), paramRule);
            if (matched) {
                // 判断是是否触发限流规则
                Integer rate = HotParamRule.getRate(paramRule);
                long cacheRate = counterCache.incrementAndGet(getCacheKey(uri, HotParamRule.getParamName(paramRule)));
                if (rate!=null && rate<cacheRate) {
                    throw new FlowException("Too Many Request");
                }
            }
        }
    }

    /**
     * 获取热点参数限流在缓存中的Key
     * @param uri 请求路径
     * @param paramName 热点参数名
     * @return
     */
    private String getCacheKey(String uri, String paramName) {
        return CacheKey.FLOW + ":hotparam:" + uri + ":" + paramName;
    }
}
